/************************************************************************
 *				SRLOS Team CopyRight
 *@file		timer.h
 *@author	bloceanc
 *@date		7/22/2010
 *@note		timer managerment
 ************************************************************************/

#include "commdef.h"
#include "task.h"
#include "timer.h"
#include "task_sigle.h"

extern k_process *g_current_process;

// be carefor deadlock
static k_timer *s_timer_list;	// all timer list.global.if operation, lock mutex
static k_mutex s_timer_mutex;	// timer list mutex

/**********************************************************************************
 * 				EXTERN FUNTIONS
 **********************************************************************************/

extern void mt_mutex_initset(k_mutex *mutex);
extern int mt_mutex_lock(k_mutex *mutex, long long time);
extern void mt_mutex_unlock(k_mutex *mutex);
extern void *kernel_allocate(unsigned int size);
extern void kernel_free(void *address);

/*
 *	initialize timer module
 */
void timer_initialize(void)
{
	s_timer_list = NULL;
	mt_mutex_initset(&s_timer_mutex);
}

/*
 *	add timer to timer list
 */
static void s_timer_insert(k_timer *timer)
{
	mt_mutex_lock(&s_timer_mutex,-1);	// wait forever
	// insert to head
	timer->next = NULL;
	timer->prev = NULL;
	if(s_timer_list == NULL)
	{
		s_timer_list = timer;
	}
	else
	{
		s_timer_list->prev = timer;
		timer->next = s_timer_list;
		s_timer_list = timer;	
	}
	// unlock
	mt_mutex_unlock(&s_timer_mutex);
}

/*
 *	delete timer from timer list
 */
static void s_timer_delete(k_timer *timer)
{
	k_timer *cur_timer = NULL;
	mt_mutex_lock(&s_timer_mutex,-1);	// wait forever
	// find the timer
	cur_timer = s_timer_list;
	while(cur_timer)
	{
		if(cur_timer == timer)
			break;
		cur_timer = cur_timer->next;
	}
	if(cur_timer != timer)
	{
		// has been deleted!
		mt_mutex_unlock(&s_timer_mutex);
		return;
	}
	if(cur_timer->next == NULL && cur_timer->prev != NULL)
	{
		// this is the last one
		cur_timer->prev->next = NULL;
	}else if(cur_timer->next == NULL && cur_timer->prev == NULL)
	{
		// this is the only one in the list
		if(cur_timer != s_timer_list)
		{
			// system error!
			return;
		}
		s_timer_list = NULL;
	}else if(cur_timer->next != NULL && cur_timer->prev != NULL)
	{
		// common one
		cur_timer->prev->next = cur_timer->next;
		cur_timer->next->prev = cur_timer->prev;
	}else if(cur_timer->next != NULL && cur_timer->prev == NULL)
	{
		// the first one
		s_timer_list = cur_timer->next;
		s_timer_list->prev = NULL;
	}
	// unlock
	mt_mutex_unlock(&s_timer_mutex);
}

/*
 *	timer interrupt process handle
 */
void irq_timer_handle(void)
{
	k_timer *timer;
	
	// then send timer single to timer owner which timer should be send
	mt_mutex_lock(&s_timer_mutex, -1);
	timer = s_timer_list;
	while(timer)
	{
		if(--timer->current_rtc == 0)
		{
			timer->current_rtc = timer->time;
			// send single
			task_process_sendsingle(timer->owner,TASK_PROCESS_SIGLE_TIMER,timer);
		}
		timer = timer->next;
	}
	mt_mutex_unlock(&s_timer_mutex);
}

/*
 *	create new timer use current process, and start timer
 */
k_timer *timer_create(long long time)
{
	k_timer *timer = (k_timer *)kernel_allocate(sizeof(k_timer));
	if(timer == NULL)
		return NULL;
	
	timer->time = timer->current_rtc = time;
	timer->owner = g_current_process;
	timer->next = NULL;
	timer->prev = NULL;
	s_timer_insert(timer);
	return timer;
}

/*
 *	cancel and delete a timer
 */
void timer_delete(k_timer *timer)
{
	if(timer == NULL)
		return;
		
	s_timer_delete(timer);
	
	// free memory
	kernel_free(timer);
}
